package com.sise.Backtracking;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;


/**
 *      93. 复原 IP 地址
 *      (与第 131 为同类型)
 *
 *      给定一个只包含数字的字符串 s ，用以表示一个 IP 地址，返回所有可能从 s 获得的 有效 IP 地址 。你可以按 任何 顺序返回答案。
 *      有效 IP 地址 正好由四个整数（每个整数位于 0 到 255 之间组成，且不能含有前导 0），整数之间用 '.' 分隔。
 *      例如："0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址，但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
 *
 *      示例 1：
 *      输入：s = "25525511135"
 *      输出：["255.255.11.135","255.255.111.35"]
 *
 */
public class _93_restoreIpAddresses {

    public List<String> restoreIpAddresses(String s) {
        int len = s.length();
        List<String> res = new ArrayList<>();
        if (len > 12 || len < 4) {
            return res;
        }

        Deque<String> path = new ArrayDeque<>(4);
        dfs(s, len, 0, 4, path, res);
        return res;
    }

    // residue：需要一个变量记录剩余多少段还没被分割
    private void dfs(String s, int len, int begin, int residue, Deque<String> path, List<String> res) {

        // 当 begin 到达终点，同时已经拥有 4个 IP 段，对 path 添加 '.' 逗号，构造IP地址的表示形式 => [1,2,3] => 1.2.3
        if (begin == len) {
            if (residue == 0) {
                res.add(String.join(".", path));
            }
            return;
        }

        // 每次从 begin 开始，值遍历三个数值
        for (int i = begin; i < begin + 3; i++) {
            if (i >= len) {
                break;
            }

            // residue 代表剩余多少段，每一段至少要有 一位数字
            if (residue * 3 < len - i) {
                continue;
            }

            // begin 代表 left 、 i 代表 right。判断为合法地址的话，那么就将其添加到 path
            if (judgeIpSegment(s, begin, i)) {
                String currentIpSegment = s.substring(begin, i + 1);
                path.addLast(currentIpSegment);

                dfs(s, len, i + 1, residue - 1, path, res);
                path.removeLast();
            }
        }
    }

    // 判断分割出来的每一段字符串是否是合法的IP地址
    private boolean judgeIpSegment(String s, int left, int right) {

        // 如果长度大于 1，同时分割出来的 第一位数 为 0，那么返回 false
        int len = right - left + 1;
        if (len > 1 && s.charAt(left) == '0') {
            return false;
        }

        // 构造 left -> right 索引位置的数值，索引下标，不断乘以 10，同时加上下一个数值，需要注意 left <= right
        int res = 0;
        while (left <= right) {
            res = res * 10 + s.charAt(left) - '0';
            left++;
        }

        // 检查该值是否在 [0,255] 区间
        return res >= 0 && res <= 255;
    }
}
